package linuxbox.bean;

import java.util.*;
import org.apache.log4j.Logger;

import linuxbox.exception.*;
import linuxbox.util.*;

abstract public class BaseBean
{
  protected static Logger logger = Logger.getLogger(BaseBean.class.getName());

  protected DbAdapter m_db = null;
  protected boolean m_bTransaction = false;

  protected Object bean;

  abstract protected void setupFields() throws Exception;
  abstract protected BeanDef getBeanDef();

  public BaseBean(DbAdapter db) {
    init(db);
  }

  public BaseBean() {
    init(null);
  }

  protected void init(DbAdapter db)
  {
    m_bTransaction= false;
    if (db != null)
    {
      m_db = db;
      m_bTransaction = true;
    }
    try
    {
      setupFields();
    }
    catch (Exception e)
    {
      logger.error("Error setupFields:",e);
    }
  }

  protected String getTableName()
  {
    return getBeanDef().getTableName();
  }

  public Object getObject()
  {
    return bean;
  }

  public void setObject(Object o)
  {
    bean = o;
  }

  public boolean isValidFieldName(String name)
  {
    if (name == null || name.length() ==0)
      return false;
    Iterator it = getBeanDef().getAllFields().values().iterator();
    while (it != null && it.hasNext())
    {
      DBField field = (DBField) it.next();
      if (name.compareToIgnoreCase(field.getFieldName()) == 0)
        return true;
    }
    return false;
  }

  protected void makeConnection() throws TableBeanException
  {
    try
    {
      if (m_db == null)
      {
        m_db = new DbAdapter(true);
      }
    }
    catch(DbBeanException e)
    {
      throw new TableBeanException("Can't make the connection: " + e.getMessage() );
    }
  }

  protected void closeConnection() throws TableBeanException
  {
    if (!m_bTransaction)
    {
      try
      {
        if (m_db != null)
        {
          m_db.close();
          m_db = null;
          if (logger.isDebugEnabled())
            logger.debug("Close the connection.\n");
        }
      }
      catch(DbBeanException dbe)
      {
        throw new TableBeanException("Can't close the connection: " + dbe.getMessage());
      }
    }
  }

  public void load() throws TableBeanException
  {
    try
    {
      StringBuffer sql = new StringBuffer();
      Iterator it = getBeanDef().getKeyFields().values().iterator();
      boolean first = true;
      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        DBUtil.setKeySqlString(bean, field, sql, first);
        first = false;
      }
      makeConnection();
      load(sql.toString());
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error load object:" + e.getMessage());
      throw new TableBeanException("Error load object:" + e.getMessage());
    }
  }

  protected void load(String whereSql) throws Exception
  {
    StringBuffer sql = new StringBuffer();
    sql.append("select * from ");
    sql.append(getTableName());

      //append the key fields
    sql.append(" where ");
    sql.append(whereSql);

    m_db.executeQuery(sql.toString());
    if (m_db.next())
    {
      Iterator it = getBeanDef().getAllFields().values().iterator();
      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        field.getValueFromDb(bean, m_db);
      }
    }
    else
    {
      logger.error("Can't find object for:" + sql.toString());
      throw new TableBeanException("Can't find object for:"+ sql.toString());
    }
  }

  public void store() throws TableBeanException
  {
    try
    {
      StringBuffer sql = new StringBuffer();
      sql.append("UPDATE ");
      sql.append(getTableName());
      sql.append(" set ");

      Iterator it = getBeanDef().getAllFields().values().iterator();
      boolean first = true;
      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        if (!field.getIsKey())
        {
          DBUtil.setSqlString(bean, field, sql, first);
          first = false;
        }
      }

      //append the key fields
      sql.append(" where ");
      it = getBeanDef().getKeyFields().values().iterator();
      first = true;
      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        DBUtil.setKeySqlString(bean, field, sql, first);
        first = false;
      }

      makeConnection();
      m_db.executeUpdate(sql.toString());
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error store object:" + e.getMessage());
      throw new TableBeanException("Error store object:" + e.getMessage());
    }
  }

  public void refresh() throws TableBeanException
  {
    load();
  }

  public void create() throws TableBeanException
  {
    try
    {
      StringBuffer sql = new StringBuffer();
      StringBuffer values = new StringBuffer();
      StringBuffer sqlWhere = new StringBuffer(); //use to load object after insert
      sql.append("INSERT INTO ");
      sql.append(getTableName());
      sql.append(" ( ");
      values.append(" VALUES(");

      Iterator it = getBeanDef().getAllFields().values().iterator();
      boolean first = true;
      boolean firstWhere = true;
      boolean beanExisted = true;
      if (bean == null)
      {
        bean = getBeanDef().getObjClass().newInstance();
        beanExisted = false;
      }

      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        if (!first)
        {
          sql.append(",");
          values.append(",");
        }
        if (!beanExisted)
        {
          // if allowNull, just set it to NULL
          if (field.getAllowNull())
          {
            sql.append(field.getFieldName());
            values.append("null");
//            DBUtil.setString(sql, field.getFieldName(), "null", first);
          }
          else
          {
            if (field.getAutoIncrement())
            {
              long max = getMax(field);
//              DBUtil.setInteger(sql, field.getFieldName(), max, first);
              sql.append(field.getFieldName());
              values.append(max);

              if (!firstWhere)
              {
                sqlWhere.append(" and ");
              }
              sqlWhere.append(field.getFieldName());
              sqlWhere.append("=");
              sqlWhere.append(max);
              firstWhere = false;
            }
            else
            {
              sql.append(field.getFieldName());
              DBUtil.appendStringValue(values, field.getDefaultValue());

              if (!firstWhere)
              {
                sqlWhere.append(" and ");
              }
              sqlWhere.append(field.getFieldName());
              sqlWhere.append("=");
              DBUtil.appendStringValue(sqlWhere, field.getDefaultValue());
              firstWhere = false;
            }
          }
        }
        else
        {
          sql.append(field.getFieldName());
          DBUtil.appendFieldValue(bean, field, values);
          if (!field.getAllowNull())
          {
            DBUtil.setKeySqlString(bean, field, sqlWhere, first);
          }
        }
        first = false;
      }
      sql.append(")");
      sql.append(values);
      sql.append(")");

      makeConnection();
      m_db.executeUpdate(sql.toString());
      load(sqlWhere.toString());
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error create object:" + e.getMessage());
      throw new TableBeanException("Error create object:" + e.getMessage());
    }
  }

  protected long getMax(DBField field) throws TableBeanException
  {
    try
    {
      StringBuffer sql = new StringBuffer();
      sql.append("select max(");
      sql.append(field.getFieldName());
      sql.append(") from ");
      sql.append(getTableName());

      makeConnection();
      m_db.executeQuery(sql.toString());
      long max_id = 0;
      if (m_db.next())
      {
        max_id = m_db.getInt(1);
      }
      max_id++;
      return max_id;
    }
    catch(Exception e)
    {
      logger.error("Error getMax for field:"+field.getFieldName(),e);
      throw new TableBeanException("Error getMax:" + e.getMessage());
    }
  }

  public void remove() throws TableBeanException
  {
    try
    {
      StringBuffer sql = new StringBuffer();
      sql.append("DELETE FROM ");
      sql.append(getTableName());
      //append the key fields
      sql.append(" WHERE ");
      Iterator it = getBeanDef().getKeyFields().values().iterator();
      boolean first = true;
      while (it != null && it.hasNext())
      {
        DBField field = (DBField) it.next();
        DBUtil.setKeySqlString(bean, field, sql, first);
        first = false;
      }

      makeConnection();
      m_db.executeUpdate(sql.toString());
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error remove object:" + e.getMessage());
      throw new TableBeanException("Error remove object:" + e.getMessage());
    }
  }

  public Collection findBySql(String sqlWhere) throws TableBeanException
  {
    ArrayList l = new ArrayList();
    StringBuffer sql = new StringBuffer();
    try
    {
      sql.append("SELECT * FROM ");
      sql.append(getTableName());
      sql.append(" WHERE ");
      sql.append(sqlWhere);

      makeConnection();
      m_db.executeQuery(sql.toString());

      if (m_db.next())
      {
        do
        {
          Object o = getBeanDef().getObjClass().newInstance();
          Iterator it = getBeanDef().getAllFields().values().iterator();
          while (it != null && it.hasNext())
          {
            DBField field = (DBField) it.next();
            field.getValueFromDb(o, m_db);
          }
          l.add(o);
        }
        while (m_db.next());
      }
      else
      {
        logger.debug("Can't findBySql object for:" + sql.toString());
      }
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error findBySql:" + sql.toString() + e.getMessage());
      throw new TableBeanException("Error findBySql object:" + e.getMessage());
    }
    return l;
  }

  public boolean updateBySql(String sql)	throws TableBeanException
  {
    try
    {
      makeConnection();
      m_db.executeUpdate(sql);
      closeConnection();
    }
    catch (Exception e)
    {
      closeConnection();
      logger.error("Error updateBySql:" + sql);
      throw new TableBeanException("Error updateBySql object:" + e.getMessage());
    }
    return true;
  }

  public int getCount() throws TableBeanException
  {
    int nCount = -1;
    String sCountSql = "select count(*) from " + getTableName();
    try
    {
      makeConnection();
      m_db.executeQuery(sCountSql);
      if (m_db.next())
        nCount = m_db.getInt(1);
      closeConnection();
    }
    catch(DbBeanException e)
    {
      closeConnection();
      logger.error("Error getCount.");
      throw new TableBeanException("Error getCount " + e.getMessage());
    }
    return nCount;
  }

  public int getCount(String strWhere) throws TableBeanException
  {
    int nCount = -1;
    StringBuffer sql = new StringBuffer();
    sql.append("SELECT COUNT(*) FROM ");
    sql.append(getTableName());
   //append the key fields
    sql.append(" WHERE ");
    sql.append(strWhere);
    try
    {
      makeConnection();
      m_db.executeQuery(sql.toString());
      if (m_db.next())
        nCount = m_db.getInt(1);
      closeConnection();
    }
    catch(DbBeanException e)
    {
      closeConnection();
      logger.error("Error getCount. SQL:" + sql.toString());
      throw new TableBeanException("Error getCount: " + e.getMessage());
    }
    return nCount;
  }
}
